﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml;

namespace ExpressesClass.SfApi.Util
{
    public class XmlUtil
    {
        #region ObjToXml
        public static string ObjToXml(object obj)
        {
            Type type = obj.GetType();
            XmlDocument doc = new XmlDocument();
            XmlElement ele = doc.CreateElement(type.Name);
            doc.AppendChild(MakeElement(doc, ele, obj, type));
            return doc.InnerXml;
        }

        private static XmlElement MakeElement(XmlDocument doc, XmlElement ele, object obj, Type type)
        {
            foreach (PropertyInfo info in type.GetProperties())
            {
                object propertyValue = obj == null ? null : info.GetValue(obj);
                Type propType = info.PropertyType;

                if (propType == typeof(string) || propType == typeof(bool) || propType == typeof(int?) || propType == typeof(double?))
                {
                    ele.SetAttribute(info.Name, propertyValue == null ? "" : propertyValue.ToString());
                }
                else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>))
                {
                    Type genericType = propertyValue.GetType().GetGenericArguments()[0];
                    int count = Convert.ToInt32(propertyValue.GetType().GetProperty("Count").GetValue(propertyValue, null));

                    for (int i = 0; i < count; i++)
                    {
                        XmlElement child = doc.CreateElement(genericType.Name);
                        object item = propertyValue.GetType().GetProperty("Item").GetValue(propertyValue, new object[] { i });
                        ele.AppendChild(MakeElement(doc, child, item, genericType));
                    }
                }
                else
                {
                    XmlElement child = doc.CreateElement(info.Name);
                    ele.AppendChild(MakeElement(doc, child, propertyValue, info.PropertyType));
                }
            }
            return ele;
        }
        #endregion

        #region XmlToObj
        public static object XmlToObj(string xml, Type type)
        {
            object result = null;
            if (xml != null && xml.Length > 0)
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(xml);
                XmlElement xe = doc.DocumentElement;
                result = convert(xe, type);
            }
            return result;
        }
        private static object convert(XmlNode node, Type type)
        {
            PropertyInfo[] properties = type.GetProperties();
            object result = type.Assembly.CreateInstance(type.FullName);

            foreach (PropertyInfo info in properties)
            {
                string propertyName = info.Name;
                Type propType = info.PropertyType;
                string val = node.Attributes[propertyName] == null ? null : node.Attributes[propertyName].Value;

                if (propType == typeof(string))
                {
                    info.SetValue(result, val);
                }
                else if (propType == typeof(int?))
                {
                    int convert = Convert.ToInt32(val);
                    info.SetValue(result, convert);
                }
                else if (propType == typeof(float?))
                {
                    float convert = Convert.ToSingle(val);
                    info.SetValue(result, convert);
                }
                else if (propType == typeof(long?))
                {
                    long convert = Convert.ToInt64(val);
                    info.SetValue(result, convert);
                }
                else if (propType == typeof(double?))
                {
                    double convert = Convert.ToDouble(val);
                    info.SetValue(result, convert);
                }
                else if (propType == typeof(bool))
                {
                    bool convert = Convert.ToBoolean(val);
                    info.SetValue(result, convert);
                }
                else if (propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(List<>))
                {

                    XmlNodeList nodeList = node.ChildNodes;
                    if (nodeList.Count > 0)
                    {
                        info.SetValue(result, convertList(nodeList, propType));
                    }
                }
                else
                {
                    XmlNodeList nodeList = node.ChildNodes;
                    if (nodeList.Count > 0)
                    {
                        info.SetValue(result, convert(nodeList[0], propType));
                    }
                }
            }
            return result;
        }
        private static object convertList(XmlNodeList nodeList, Type type)
        {
            object result = type.Assembly.CreateInstance(type.FullName);
            Type genericType = type.GetGenericArguments()[0];
            MethodInfo methodinfo = type.GetMethod("Add");
            foreach (XmlNode xn in nodeList)
            {
                if (genericType == typeof(string))
                {
                    methodinfo.Invoke(result, new object[] { xn == null ? null : xn.InnerText });
                }
                else
                {
                    methodinfo.Invoke(result, new object[] { convert(xn, genericType) });
                }
            }
            return result;
        }
        #endregion
    }
}
